package ss

import (
	"fmt"
	"reflect"
	"strconv"
	"sync"

	// "gf-tools/mutex"
	"gitee.com/AlmondLee/gf-tools/mutex"

	"github.com/gogf/gf/debug/gdebug"
	"github.com/gogf/gf/encoding/gjson"

	// "github.com/gogf/gf/os/glog"
	"github.com/gogf/gf/os/gfile"
	"github.com/gogf/gf/os/gtime"
	"github.com/gogf/gf/util/gconv"
)

var PrintStdout bool

type Lg struct {
	PrintStdout bool
	fileName	string
	mutex		sync.Mutex
}

// var WarnLog *Lg = &Lg{
//	PrintStdout: false,
//	fileName:	 `warn.log`,
// }

// var ErrLog *Lg = &Lg{
//	PrintStdout: false,
//	fileName:	 `err.log`,
// }

// var RecLog *Lg = &Lg{
//	PrintStdout: false,
//	fileName:	 `rec.log`,
// }

// var LlLog *Lg = &Lg{
//	PrintStdout: false,
//	fileName:	 `ll.log`,
// }

var WarnLog *Lg = &Lg{
	PrintStdout: false,
	fileName:	 `warn.log`,
}

var ErrLog *Lg = &Lg{
	PrintStdout: false,
	fileName:	 `err.log`,
}

var RecLog *Lg = &Lg{
	PrintStdout: false,
	fileName:	 `rec.log`,
}

var LlLog *Lg = &Lg{
	PrintStdout: false,
	fileName:	 `ll.log`,
}

func (this *Lg) printOut(str string) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()

	this.mutex.Lock()
	// defer func() {
	defer this.mutex.Unlock()
	// }()

	gfile.PutContentsAppend(this.fileName, startF()+str+endF())

	if PrintStdout {
		fmt.Println(str)
	}

}

// type ll struct{
// Lg
// =`ll.log`
// }

// func ll() *ll{
// }

// var stdoutChan chan bool = make(chan bool)
// var stdoutChan *mutex.Mutex = mutex.New(1)
// var stdoutMutex *sync.Mutex = new(sync.Mutex)

var startFlag string = fmt.Sprint(".\n.\n.\n-----------------BEGIN-------------------\n")
var endFlag string = fmt.Sprint("\n__________________END___________________\n.\n.\n.")

// func init() {
// go func() { stdoutChan <- false }()
// }
func endF() string {

	// glog.Print(endFlag)
	// stdout :=
	return fmt.Sprintln("\n\t", gtime.Now().ISO8601(), endFlag)
	// if PrintStdout {
	// fmt.Println(stdout)
	// }

	// fmt.Sprint("____________________________________")
}
func startF() string {
	// fmt.Print(startFlag)
	// glog.Println("\n")
	return fmt.Sprintln("\n", startFlag, "\t", gtime.Now().ISO8601())

	// fmt.Sprint("____________________________________")
}

func Gettype(obj interface{}) reflect.Type {
	// defer Recover()
	defer func() {
		if err := recover(); err != nil {
			if PrintStdout {
				fmt.Println(err)
			}
		}
	}()

	return reflect.TypeOf(obj)
}

func GetType(obj interface{}) string {
	return fmt.Sprintf("%T", obj)
}

// var PL func(...interface{}) = Pl
var Pl = (*Lg).PL
var P = LlLog.P
var ERR = ErrLog.Err
var WARN = WarnLog.Warn
var Rec = RecLog.P

func (this *Lg) PL(output ...interface{}) {
	this.printOut(fmt.Sprintln(`来自`, Stark(2), "\n输出:\n", fmt.Sprintln(output...)))
}

// var PD func(...interface{}) = Pd
// var E func(...interface{}) = Pd

var E = (*Lg).Pd
var PD = (*Lg).Pd

// func Pd(output ...interface{}) {
func (this *Lg) Pd(output ...interface{}) {

	// fmt.Println(`来自`, Stark(2)) //+`:`)
	// fmt.Println(`段落输出:`)
	// fmt.Println(`段落输出(`+Stark(2)+`):`)
	// fmt.Println()
	str := fmt.Sprint(`来自`, Stark(2), "\n输出:\n\n")
	// for argId, arg := range output {
	length := len(output) - 1

	for i := 0; i <= length; i++ {
		// fmt.Println("\targ", argId, `:`)
		// fmt.Print(Dis(arg))
		// Display(output[i])
		j := gjson.New(output[i], false)
		ot, err := j.ToJsonIndentString()
		if err != nil {
			str += fmt.Sprintln(`变量无法json化`)
			str += fmt.Sprintln(`变量:`, output[i])
			str += fmt.Sprintln(`原因:`, err)
			str += fmt.Sprintln()
			continue
		}
		str += fmt.Sprintln(ot)

		if i != length {
			str += "\n"
		}
	}
	this.printOut(str)

	// fmt.Println(`------------------------------------`)
	// fmt.Print("\n")
}

func (this *Lg) Fatal(err interface{}) {
	// func Fatal(err interface{}) {
	// if err != nil {

	this.printOut(fmt.Sprintln(`发生错误:`, "\n\t\t", err) + fmt.Sprintln("\t", gdebug.Stack(1)))
	// fmt.Print("\n")
	panic(``)
	// }
}

// var ERR func(error) = Err

// var ERR = (*Lg).Err

func (this *Lg) Err(err error) {
	// func Err(err error) {
	if err != nil {
		this.printOut(fmt.Sprintln(`发生错误:`, "\n\t\t", err.Error()) + fmt.Sprintln("\t", gdebug.Stack(1)))
		panic(``)
	}
	// fmt.Print("\n")
}

// var WARN func(error) = Warn
// var WARN = (*Lg).Warn

// func Warn(err error) {
func (this *Lg) Warn(err error) {
	// defer Recover()
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()

	if err != nil {

		this.printOut(fmt.Sprintln(`警告:`, "\n\t", err.Error()) + fmt.Sprintln("\t\t", gdebug.Stack(1)))

	}
}

func Stark(skip int) (lineInf string) {
	// defer Recover()
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()

	function, path, line := gdebug.Caller(skip)
	return fmt.Sprint(function, path, ` `, line) + `:`
}

func PS(inputs ...interface{}) (conv []string) {
	// defer Recover()
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()
	// stdoutMutex.Lock()
	// defer func() { go func() { stdoutChan <- false }() }()
	// defer stdoutMutex.Unlock()
	conv = make([]string, 0)
	// fmt.Print("\n")
	length := len(inputs)
	// if length== 1 {
	// fmt.Println(inputs[0])
	// } else {
	for i := 0; i < length; i++ {
		if slice, ok := inputs[i].([]byte); ok { // checked type assertion
			conv = append(conv, string(slice))
		} else {
			// fmt.Println(slice)
			conv = append(conv, fmt.Sprint(inputs[i]))
		}
	}
	// }

	// fmt.Println(string(i))

	// fmt.Print("\n")
	return
}

// func PS(i []byte) {
//	stdoutMutex.Lock()
//	// defer func() { go func() { stdoutChan <- false }() }()
//	defer stdoutMutex.Unlock()

//	fmt.Print("\n")

//	fmt.Println(string(i))

//	fmt.Print("\n")
// }
// func (this *Lg) Warn(err error) {
func (this *Lg) P(i ...interface{}) {
	// defer Recover()
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()

	this.printOut(fmt.Sprintln(i...))
	// if len(i) == 1 {
	// fmt.Println("\t", i[0])
	// } else {
	// fmt.Println(i)
	// for _, val := range PS(i) {
	// fmt.Print("\t", val, ``)
	// }
	// }
}

func (this *Lg) Panic(i ...interface{}) {

	panic(i)

}

// func Display(x interface{}, name ...string) string {
func Dis(x interface{}) string {
	// defer Recover()
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()
	// if len(name) == 0 {
	// name = make([]string, 1)
	// name[0] = ``
	// }
	v := reflect.ValueOf(x)
	// name[0] = "\t" + name[0]
	// name[0] = "\t" + v.
	// fmt.Printf("Display %s (%T):\n", name[0], x)
	return dis(v, "\t")
}

func dis(v reflect.Value, returnInf string) string {
	// defer Recover()
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()
	// if returnInf == `` {
	// returnInf = "\t"
	// }

	switch v.Kind() {
	case reflect.Invalid:
		return fmt.Sprintf("%s\tinvalid\n", returnInf)
	case reflect.Slice, reflect.Array:
		for i := 0; i < v.Len(); i++ {
			// returnInf += "\n\t"
			dis(v.Index(i), fmt.Sprintf("%sarray[%d]:\n\t", returnInf, i))
		}
	case reflect.Struct:
		for i := 0; i < v.NumField(); i++ {
			returnInf += fmt.Sprintf("%s.%s:\n\t", returnInf, v.Type().Field(i).Name)
			dis(v.Field(i), returnInf)
		}
	case reflect.Map:
		for _, key := range v.MapKeys() {
			// returnInf += "\n\t"
			dis(v.MapIndex(key), fmt.Sprintf("%s[%s]:\n\t", returnInf,
				formatAtom(key)))
		}
	case reflect.Ptr:
		if v.IsNil() {
			returnInf += fmt.Sprintf("%s\tnil", returnInf)
		} else {
			// returnInf += "\n\t"
			dis(v.Elem(), fmt.Sprintf("(*%s):", returnInf))
		}
	case reflect.Interface:
		if v.IsNil() {
			returnInf += fmt.Sprintf("%s\tnil\n", returnInf)
		} else {
			returnInf += fmt.Sprintf("%s\t(%s):", returnInf, v.Elem().Type())
			returnInf += "\n\t"
			dis(v.Elem(), returnInf+".value")
		}
	default: // basic types, channels, funcs
		returnInf += fmt.Sprintf("%s\t%s\n", returnInf, formatAtom(v))
	}
	return "\n"
}

func Display(x interface{}) {
	// defer Recover()
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()

	fmt.Printf("\t输出变量.(%T):\n", x)
	// display(`变量`, reflect.ValueOf(x))
	fmt.Print(displayf(`变量`, reflect.ValueOf(x), ""))
}

func displayf(path string, v reflect.Value, tab string) (returnInf string) {
	// defer Recover()
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()

	tab += "\t"
	returnInf += tab
	// fmt.Println(len(tab))
	switch v.Kind() {
	case reflect.Invalid:
		return fmt.Sprintf("%s%s invalid\n", tab, path)
	case reflect.Slice, reflect.Array:
		for i := 0; i < v.Len(); i++ {
			returnInf += displayf(fmt.Sprintf("%s[%d]:\n%s", path, i, tab), v.Index(i), tab)
		}
	case reflect.Struct:
		for i := 0; i < v.NumField(); i++ {
			fieldPath := fmt.Sprintf("%s%s.%s:\n", tab, path, v.Type().Field(i).Name)
			returnInf += displayf(fieldPath, v.Field(i), tab)
		}
	case reflect.Map:
		for _, key := range v.MapKeys() {
			displayf(fmt.Sprintf("%s%s[%s]:\n", tab, path,
				formatAtom(key)), v.MapIndex(key), tab)
		}
	case reflect.Ptr:
		if v.IsNil() {
			returnInf += fmt.Sprintf("%s%s = nil\n", tab, path)
		} else {
			returnInf += displayf(fmt.Sprintf("%s(*%s)", tab, path), v.Elem(), tab)
		}
	case reflect.Interface:
		if v.IsNil() {
			returnInf += fmt.Sprintf("%s%s = nil\n", tab, path)
		} else {
			returnInf += fmt.Sprintf("%s%s.type = %s\n", tab, path, v.Elem().Type())
			returnInf += displayf(tab+path+".value", v.Elem(), tab)
		}
	default: // basic types, channels, funcs
		returnInf += fmt.Sprintf("%s%s %s\n", tab, path, formatAtom(v))
	}
	return returnInf
}

func display(path string, v reflect.Value) {
	// defer Recover()
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()

	switch v.Kind() {
	case reflect.Invalid:
		fmt.Printf("%s = invalid\n", path)
	case reflect.Slice, reflect.Array:
		for i := 0; i < v.Len(); i++ {
			display(fmt.Sprintf("%s[%d]", path, i), v.Index(i))
		}
	case reflect.Struct:
		for i := 0; i < v.NumField(); i++ {
			fieldPath := fmt.Sprintf("%s.%s", path, v.Type().Field(i).Name)
			display(fieldPath, v.Field(i))
		}
	case reflect.Map:
		for _, key := range v.MapKeys() {
			display(fmt.Sprintf("%s[%s]", path,
				formatAtom(key)), v.MapIndex(key))
		}
	case reflect.Ptr:
		if v.IsNil() {
			fmt.Printf("%s = nil\n", path)
		} else {
			display(fmt.Sprintf("(*%s)", path), v.Elem())
		}
	case reflect.Interface:
		if v.IsNil() {
			fmt.Printf("%s = nil\n", path)
		} else {
			fmt.Printf("%s.type = %s\n", path, v.Elem().Type())
			display(path+".value", v.Elem())
		}
	default: // basic types, channels, funcs
		fmt.Printf("%s = %s\n", path, formatAtom(v))
	}
}

func formatAtom(v reflect.Value) string {
	// defer Recover()
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()

	switch v.Kind() {
	case reflect.Invalid:
		return "invalid"
	case reflect.Int, reflect.Int8, reflect.Int16,
		reflect.Int32, reflect.Int64:
		return strconv.FormatInt(v.Int(), 10)
	case reflect.Uint, reflect.Uint8, reflect.Uint16,
		reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return strconv.FormatUint(v.Uint(), 10)
	// ...floating-point and complex cases omitted for brevity...
	case reflect.Bool:
		return strconv.FormatBool(v.Bool())
	case reflect.String:
		return strconv.Quote(v.String())
	case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Slice, reflect.Map:
		return v.Type().String() + " 0x" +
			strconv.FormatUint(uint64(v.Pointer()), 16)
	default: // reflect.Array, reflect.Struct, reflect.Interface
		return v.Type().String() + " value"
	}
}

func Recover() {
	defer func() {
		fmt.Println(`recover:`)
		if err := recover(); err != nil {
			fmt.Println(`recover:`, err)
		}
	}()
}

func For(count interface{}, function func()) {
	for i := 0; i < gconv.Int(count); i++ {
		function()
	}

}

func ForInt(count interface{}, function func(int)) {
	for i := 0; i < gconv.Int(count); i++ {
		function(i)
	}

}

func LockUpdate(lock *mutex.Mutex, function func()) {
	// for i := 0; i < gconv.Int(count); i++ {
	lock.Lock()
	defer lock.UnLock()
	function()

	// }

}
